package com.skyline.courier.net;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.skyline.common.utils.Assert;

public abstract class AbstractClient extends EndPoint implements Client {
	private static final Logger LOGGER = LoggerFactory.getLogger(AbstractClient.class);
	protected ConnectionManager connectionManager;
	protected Statistic statistic;
	protected volatile boolean running = false;
	protected Integer flowThreshold = -1;
	protected Integer flowTimeout = 0;
	protected Integer completeTimeout = 0;
	protected SocketAddress[] addresses;
	protected LoadBalancer loadBalancer = new RoundRobinBalancer();
	protected volatile boolean connectionInited = false;

	@Override
	public Connection connect(SocketAddress address) throws IOException {
		if(!running) {
			LOGGER.info("客户端尚未启动，即将启动客户端");
			start();
		}
		
		Connection connection = connectionManager.getConnection(address);
		if(connection != null) {
			LOGGER.info("已存在链接到[" + address + "]的connection["+ connection +"]，直接返回");
			return connection;
		}
		
		connection = connectionManager.getDisconnect(address);
		if(connection != null) {
			LOGGER.info("存在已断开的到[" + address + "]的connection["+ connection +"]，重连并返回");
			connection.reconnect();
			return connection;
		}
		
		connection = getConnection(address);
		
		return connection;
	}
	
	protected Connection getConnection(SocketAddress address) {
		LOGGER.info("不存在到[" + address + "]的connection，开始创建链接...");
		Connection connection = doGetConnection(address);
		
		connection.setRemoteAddress(address);
		connection.setConnectionManager(connectionManager);
		connection.setStatistic(statistic);
		connection.setFlowThreshold(flowThreshold);
		connection.setCompeleteTimeout(completeTimeout );
		
		connectionManager.addConnected(address, connection);
		
		LOGGER.info("创建到[" + address + "]的connection["+ connection +"]成功");
		return connection;
	}


	@Override
	public synchronized void start() {
		try {
			LOGGER.info("客户端开始启动...");
			initManager();
			doStart();
			running = true;
			LOGGER.info("客户端启动成功");
		} catch (Throwable e) {
			LOGGER.info("客户端启动失败", e);
			close();
		}
		
	}

	private void initManager() {
		connectionManager = new ConnectionManager();
		statistic = new Statistic();
	}
	
	
	@Override
	public Connection connect() throws IOException {
		Assert.notNull(addresses, "地址列表未初始化，请先设置hosts");
		if(!running) {
			LOGGER.info("客户端尚未启动，即将启动客户端");
			start();
		}
		
		if(!connectionInited) {
			initConnections();
		}
		
		Connection connection = loadBalancer.next();
		if(connection == null) {
			return connect(addresses[0]);
		}
		return connection;
	}
	
	private void initConnections() {
		for(SocketAddress address : addresses) {
			try {
				connect(address);
			} catch(IOException e) {
				LOGGER.info("创建到["+ address +"]的链接失败", e);
				connectionManager.addDisconnectAddress(address, null);
			}
		}
		loadBalancer.setConnectionManager(connectionManager);
		connectionInited = true;
	}
	
	@Override
	public void setHosts(String hosts) {
		addresses = parseAddresses(hosts);
	}
	
	private InetSocketAddress parseAddress(String addressString) {
		int idx = addressString.indexOf(':');
		Assert.isTrue(idx != -1, "地址[" + addressString + "]不包含端口号");
		return new InetSocketAddress(addressString.substring(0, idx),
				Integer.parseInt(addressString.substring(idx + 1)));
	}

	private SocketAddress[] parseAddresses(String addresses) {
		String[] addrs = addresses.split(" *, *");
		SocketAddress[] res = new SocketAddress[addrs.length];
		for (int i = 0; i < addrs.length; i++) {
			res[i] = parseAddress(addrs[i]);
		}
		return res;
	}
	
	public void setFlowThreshold(Integer flowThreshold) {
		this.flowThreshold = flowThreshold;
	}
	
	public void setFlowTimeout(Integer flowTimeout) {
		Assert.isTrue(flowTimeout >= 0, "flowTimeout不能小于0");
		this.flowTimeout = flowTimeout;
	}
	
	public void setCompleteTimeout(Integer completeTimeout) {
		this.completeTimeout = completeTimeout;
	}
	
	@Override
	public synchronized void close() {
		LOGGER.info("客户端开始关闭...");
		doClose();
		running = false;
		LOGGER.info("客户端关闭成功");
	}
	
	protected abstract void doStart();
	
	protected abstract Connection doGetConnection(SocketAddress address);
	
	protected abstract void doClose();
}
